﻿Imports System
Imports System.Text
Imports System.Threading
Imports System.Collections.Generic
Imports Microsoft.VisualStudio.TestTools.UnitTesting

Imports NoDeadLockReadWriteLocker

<TestClass()> _
Public Class UnitTest1
	Implements ICloneable

	#Region "Members"
    Private testContextInstance As TestContext

		Private _IsBusy As Integer = 0

		Private _List  As New List(Of Integer)()
		Private _Lock  As New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks)
		Private _Locks As New List(Of ReadWriteLocker)( _
			New ReadWriteLocker() {                       _
			New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks),                    _
			New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks),                    _
			New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks),                    _
			New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks),                    _
			New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks),                    _
			New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks),                    _
			New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks),                    _
			New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks),                    _
			New ReadWriteLocker(ReadWriteLocker.Behavior.NoDeadLocks)})
	#End Region

	#Region "Properties"
		'''<summary>
		'''Gets or sets the test context which provides
		'''information about and functionality for the current test run.
		'''</summary>
		Public Property TestContext() As TestContext
				Get
						Return testContextInstance
				End Get
				Set(ByVal value As TestContext)
						testContextInstance = Value
				End Set
		End Property
	#End Region

	#Region "Additional test attributes"
			'
			' You can use the following additional attributes as you write your tests:
			'
			' Use ClassInitialize to run code before running the first test in the class
			' <ClassInitialize()> Public Shared Sub MyClassInitialize(ByVal testContext As TestContext)
			' End Sub
			'
			' Use ClassCleanup to run code after all tests in a class have run
			' <ClassCleanup()> Public Shared Sub MyClassCleanup()
			' End Sub
			'
			' Use TestInitialize to run code before running each test
			' <TestInitialize()> Public Sub MyTestInitialize()
			' End Sub
			'
			' Use TestCleanup to run code after each test has run
			' <TestCleanup()> Public Sub MyTestCleanup()
			' End Sub
			'
	#End Region

  <TestMethod()> _
	Public Sub SimpleTestMethod()
		Using ReadLock As IDisposable = _Lock.GetReadLock()
			If _Lock.IsSuperThreadRunning Then
				Diagnostics.Debug.Fail("SuperThread invoked with single thread environment.")
			End If
		End Using

		Using WriteLock As IDisposable = _Lock.GetWriteLock()
			If _Lock.IsSuperThreadRunning Then
				Diagnostics.Debug.Fail("SuperThread invoked with single thread environment.")
			End If
		End Using

		Using ReadLock As IDisposable = _Lock.GetReadLock()
			Using WriteLock As IDisposable = _Lock.GetWriteLock()
				If _Lock.IsSuperThreadRunning Then
					Diagnostics.Debug.Fail("SuperThread invoked with single thread environment.")
				End If
			End Using
		End Using

		Using WriteLock As IDisposable = _Lock.GetWriteLock()
			Using ReadLock As IDisposable = _Lock.GetReadLock()
				If _Lock.IsSuperThreadRunning Then
					Diagnostics.Debug.Fail("SuperThread invoked with single thread environment.")
				End If
			End Using
		End Using

		Using ReadLock As IDisposable = _Lock.GetReadLock()
			Using WriteLock As IDisposable = _Lock.GetWriteLock()

				Using WriteLock1 As IDisposable = _Lock.GetWriteLock()
					Using ReadLock1 As IDisposable = _Lock.GetReadLock()
						If _Lock.IsSuperThreadRunning Then
							Diagnostics.Debug.Fail("SuperThread invoked with single thread environment.")
						End If
					End Using
				End Using

			End Using
		End Using

		Using WriteLock As IDisposable = _Lock.GetWriteLock()
			Using ReadLock As IDisposable = _Lock.GetReadLock()

				Using WriteLock1 As IDisposable = _Lock.GetWriteLock()
					Using ReadLock1 As IDisposable = _Lock.GetReadLock()
						If _Lock.IsSuperThreadRunning Then
							Diagnostics.Debug.Fail("SuperThread invoked with single thread environment.")
						End If
					End Using
				End Using

			End Using
		End Using

		Using WriteLock As IDisposable = _Lock.GetWriteLock()
			Using ReadLock As IDisposable = _Lock.GetReadLock()

				Using ReadLock1 As IDisposable = _Lock.GetReadLock()
					Using WriteLock1 As IDisposable = _Lock.GetWriteLock()

					End Using
				End Using

			End Using
		End Using
  End Sub

	Private Function CloneObject() As Object _
		Implements ICloneable.Clone

		Return Clone()
	End Function

	Public Function Clone As UnitTest1
		Dim ClonedUnitTest1 As New UnitTest1()

		ClonedUnitTest1._List  = _List
		ClonedUnitTest1._Lock  = _Lock
		ClonedUnitTest1._Locks = _Locks

		Return ClonedUnitTest1
	End Function

  <TestMethod()> _
	Public Sub TestMethod()
		PerformRandomTest()
  End Sub

	Public Sub PerformRandomTestInClonedUnitTest()
		For Index As Integer = 0 To 17
			Clone().StartRandomTest()
		Next

		While (_IsBusy > 0)
			Thread.Sleep(500)
		End While
	End Sub

	Public Sub PerformRandomTest()
		For Index As Integer = 0 To 25
			StartRandomTest()
			'Thread.Sleep(3000)
		Next

		While (_IsBusy > 0)
			Thread.Sleep(500)
		End While
	End Sub

	Public Sub StartRandomTest()
		Interlocked.Increment(_IsBusy)
		ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf ProcessRandomTest))
	End Sub

	Public Sub ProcessRandomTest(context As Object)
		If (New Random()).Next(0, 2) = 1 Then
			Using WriteLock As IDisposable = _Lock.GetWriteLock()
				Static Dim CheckLock1 As Integer = 0

				Interlocked.Increment(CheckLock1)
				Try
					#If DEBUG Then
						If Not _Lock.IsCurrentThreadLockedForWriting() Then
							Assert.Fail("Current Writing Thead is not this thread.")
						End If
					#End If

					For Index As Integer = 0 To 500
						If CheckLock1 > 1 Then
							Assert.Fail("Multiple threads within a write lock:  " & CheckLock1 & ".")
						End If

						_List.Add(Index)

						Thread.Sleep((New Random()).Next(1, 3))
					Next
				Finally
					Interlocked.Decrement(CheckLock1)
				End Try
			End Using

			Using ReadLock As IDisposable = _Lock.GetReadLock()
				#If DEBUG Then
					If Not _Lock.IsCurrentThreadLockedForReading() Then
						Assert.Fail("Current Reading Thead is not this thread.")
					End If
				#End If

				For Each Index As Integer In _List
					'Thread.Sleep((New Random()).Next(1, 3))
				Next
			End Using

			Using RndLock As IDisposable = GetRandomLock()
				Using ReadLock As IDisposable = _Lock.GetReadLock()
					#If DEBUG Then
						If Not _Lock.IsCurrentThreadLockedForReading() Then
							Assert.Fail("Current Reading Thead is not this thread.")
						End If
					#End If

					For Each Index As Integer In _List
						'Thread.Sleep((New Random()).Next(1, 3))
					Next
				End Using

				Using WriteLock As IDisposable = _Lock.GetWriteLock()
					Static Dim CheckLock2 As Integer = 0

					Interlocked.Increment(CheckLock2)
					Try
						#If DEBUG Then
							If Not _Lock.IsCurrentThreadLockedForWriting() Then
								Assert.Fail("Current Writing Thead is not this thread.")
							End If
						#End If

						For Index As Integer = 0 To 500
							If CheckLock2 > 1 Then
								Assert.Fail("Multiple threads within a write lock:  " & CheckLock2 & ".")
							End If

							_List.Remove(Index)

							Thread.Sleep((New Random()).Next(1, 3))
						Next
					Finally
						Interlocked.Decrement(CheckLock2)
					End Try
				End Using
			End Using
		Else
			Using ReadLock As IDisposable = _Lock.GetReadLock()
				#If DEBUG Then
					If Not _Lock.IsCurrentThreadLockedForReading() Then
						Assert.Fail("Current Reading Thead is not this thread.")
					End If
				#End If

				For Each Index As Integer In _List
					'Thread.Sleep((New Random()).Next(1, 3))
				Next
			End Using

			Using WriteLock As IDisposable = _Lock.GetWriteLock()
				Static Dim CheckLock3 As Integer = 0

				Interlocked.Increment(CheckLock3)

				Try
					#If DEBUG Then
						If Not _Lock.IsCurrentThreadLockedForWriting() Then
							Assert.Fail("Current Writing Thead is not this thread.")
						End If
					#End If

					For Index As Integer = 0 To 500
						If CheckLock3 > 1 Then
							Assert.Fail("Multiple threads within a write lock:  " & CheckLock3 & ".")
						End If

						_List.Add(Index)

						Thread.Sleep((New Random()).Next(1, 3))
					Next
				Finally
					Interlocked.Decrement(CheckLock3)
				End Try
			End Using

			Using RndLock As IDisposable = GetRandomLock()
				Using WriteLock As IDisposable = _Lock.GetWriteLock()
					Static Dim CheckLock4 As Integer = 0

					Interlocked.Increment(CheckLock4)

					Try
						#If DEBUG Then
							If Not _Lock.IsCurrentThreadLockedForWriting() Then
								Assert.Fail("Current Writing Thead is not this thread.")
							End If
						#End If

						For Index As Integer = 0 To 500
							If CheckLock4 > 1 Then
								Assert.Fail("Multiple threads within a write lock:  " & CheckLock4 & ".")
							End If

							_List.Remove(Index)

							Thread.Sleep((New Random()).Next(1, 3))
						Next
					Finally
						Interlocked.Decrement(CheckLock4)
					End Try
				End Using

				Using ReadLock As IDisposable = _Lock.GetReadLock()
					#If DEBUG Then
						If Not _Lock.IsCurrentThreadLockedForReading() Then
							Assert.Fail("Current Reading Thead is not this thread.")
						End If
					#End If

					'If Not _Lock.IsCurrentThreadLockedForReadingAndAnotherThreadLockedForWriting() Then
					'	Assert.Fail("Bad.")
					'End If

					For Each Index As Integer In _List
						'Thread.Sleep((New Random()).Next(1, 3))
					Next
				End Using
			End Using
		End If

		Using WriteLock As IDisposable = _Lock.GetWriteLock()
			Using RndLock As IDisposable = GetRandomLock()
				Static Dim CheckLock5 As Integer = 0

				Interlocked.Increment(CheckLock5)

				Try
					#If DEBUG Then
						If Not _Lock.IsCurrentThreadLockedForWriting() Then
							Assert.Fail("Current Writing Thead is not this thread.")
						End If
					#End If

					For Index As Integer = 0 To 500
						If CheckLock5 > 1 Then
							Assert.Fail("Multiple threads within a write lock:  " & CheckLock5 & ".")
						End If

						_List.Remove(Index)

						Thread.Sleep((New Random()).Next(1, 3))
					Next
				Finally
					Interlocked.Decrement(CheckLock5)
				End Try
			End Using
		End Using

		Using ReadLock As IDisposable = _Lock.GetReadLock()
			Using RndLock As IDisposable = GetRandomLock()
				#If DEBUG Then
					If Not _Lock.IsCurrentThreadLockedForReading() Then
						Assert.Fail("Current Reading Thead is not this thread.")
					End If
				#End If

				'If Not _Lock.IsCurrentThreadLockedForReadingAndAnotherThreadLockedForWriting() Then
				'	Assert.Fail("Bad.")
				'End If

				For Each Index As Integer In _List
					'Thread.Sleep((New Random()).Next(1, 3))
				Next
			End Using
		End Using

		Interlocked.Decrement(_IsBusy)
	End Sub

	Public Function GetRandomLock() As IDisposable
		Dim LockHandles As New List(Of IDisposable)()

		For Each Lock As ReadWriteLocker In _Locks
			If (New Random()).Next(0, 2) = 1 Then
				LockHandles.Add(Lock.GetReadLock())
			Else
				LockHandles.Add(Lock.GetWriteLock())
			End If
		Next

		Return New DisposableArray(LockHandles)
	End Function
End Class
